"""
start 是准备好了，等待系统cpu调度；join是主线程等子线程完成
setDaemon, 设置守护线程，主线程不等，主线程执行完后，子线程也自动关闭；默认是false
sleep 其实就表示cpu可以空闲了，相当于模拟io操作，相当人工触发切换cpu上下文

【守护线程的定义和作用】：守护线程通常用于执行一些不需要独立存在的任务，例如垃圾回收等后台服务。它们的特点是在主进程结束时自动结束，不会阻碍主进程的退出‌
当一个线程被设置为守护线程时，意味着这个线程不重要，主线程在结束时不需要等待守护线程完成其任务。如果主线程结束了，守护线程也会被强制结束‌
"""
import threading
import pytest

import time

loop = 10000000
number = 0
lock_object = threading.RLock()


def __add2(count):
    # time.sleep(0.01)
    global number
    name = threading.current_thread().getName()
    print(f"{name} is doing task")
    for i in range(count):
        number +=1
        print(number, end=" ")


def __add(count):
    # time.sleep(0.01)
    global number
    name = threading.current_thread().getName()
    print(f"{name} is doing task")
    for i in range(count):
        number +=1

def __add_lock (count):
    global number
    lock_object.acquire()  # 申请或等待
    print("is doing task")
    for i in range(count):
        number +=1
    lock_object.release()


def __sub(count):
    global number
    for i in range(count):
        number -=1


def __sub_lock(count):
    lock_object.acquire()  # 必须是线程间是同一把锁，才会形成争抢
    global number
    for i in range(count):
        number -=1
    lock_object.release()





def task2(obj):
    print("args:",obj)
    print(obj[0])
    print("do task2")


class MyThread(threading.Thread):
    def run(self):
        print("doing task using class definition")
        task2(self._args)


def test_one_thread():
    t1 = threading.Thread(target=__add2, args=(100000,))
    t1.setName("Tony")
    t1.setDaemon(False)
    # set语句必须在start之前
    t1.start()
    # 没有join,估计默认一次cpu时间分片的时间
    # t1.join()
    print()
    print("end:", number)


def test_sequence():
    t1 = threading.Thread(target=__add, args=(loop,))
    t2 = threading.Thread(target=__sub, args=(loop,))
    t1.start()
    t1.join()
    t2.start()
    t2.join()
    print(number)


def test_concurrent():
    t1 = threading.Thread(target=__add, args=(loop,))
    t2 = threading.Thread(target=__sub, args=(loop,))
    t1.start()
    t2.start()
    t1.join()
    t2.join()
    print(number)


def test_daemon():
    t1 = threading.Thread(target=task1)
    t1.setDaemon(False)
    t1.start()
    # t1.join() # 相对daemon设置，join优先级更高
    print("END")


def test_class_threading():
    t = MyThread(args=(100,))
    t.start()


# 解决因为多丝程操作一个变量，导致数据混乱的问题，加锁python线程有锁机制；或者用redis的锁
def test_concurrent_lock():
    t1 = threading.Thread(target=__add_lock, args=(loop,))
    t2 = threading.Thread(target=__sub_lock, args=(loop,))
    t1.start()
    t2.start()
    t1.join()
    t2.join()
    print(number)


# 相同任务线程，也会产生产数据混乱，也要加锁
def task3(count):
    ## 锁确保了执行完循环才释放
    with lock_object: # 支持上下文件语法
        global number
        name = threading.current_thread().getName()
        print(f"{name} is doing task")
        for i in range(count):
            number +=1
            print(number, end=" ")
        print(number)


def test_repeat_thread_lock():
    for i in range(2):
        t = threading.Thread(target=task3, args=(100,))
        t.start()

    # print("end:", number)


# 【线程安全数据类型】自身带锁的数据类型 或 本来就不存在受并发干扰 如：列表的append
# lock与rlock的区别：rlock支持嵌套，减少死锁风险； lock效率更高
data_list = []
def task4(count):
        global number
        name = threading.current_thread().getName()
        print(f"{name} is doing task")
        for i in range(count):
            data_list.append(i)
        print(len(data_list))


def test_repeat_thread_safe():
    for i in range(2):
        t = threading.Thread(target=task4, args=(1000000,))
        t.start()


"""
 死锁的场景：
 1. lock锁嵌套
 2。多把锁交错，并出现相互等待
 线程池：
 有节制控制cpu上下文消耗，控制线数数量，引入线程池重用机制
 """